package top.ezzz.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.system.oshi.CpuInfo;
import cn.hutool.system.oshi.OshiUtil;
import lombok.extern.slf4j.Slf4j;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HWDiskStore;
import oshi.hardware.NetworkIF;
import oshi.software.os.OperatingSystem;
import top.ezzz.model.SystemInfo;

import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class SystemInfoUtil {

    public static SystemInfo getSystemInfo() {
        SystemInfo result = new SystemInfo();
        CentralProcessor processor = OshiUtil.getHardware().getProcessor();
        GlobalMemory memory = OshiUtil.getHardware().getMemory();
        OperatingSystem os = OshiUtil.getOs();
        List<NetworkIF> networkIFs = OshiUtil.getHardware().getNetworkIFs();
        List<HWDiskStore> diskStores = OshiUtil.getHardware().getDiskStores();

        // CPU
        CpuInfo cpuInfo = OshiUtil.getCpuInfo();
        result.setCpu(new SystemInfo.Cpu()
                .setModel(processor.getProcessorIdentifier().getName())
                .setUsedPercent(NumberUtil.round(cpuInfo.getUsed(), 2).doubleValue())
                .setCoreCount(processor.getLogicalProcessorCount())
        );

        // Memory
        long totalMemory = memory.getTotal();
        long availableMemory = memory.getAvailable();
        long usedMemory = totalMemory - availableMemory;
        result.setMemory(new SystemInfo.Memory()
                .setTotal(FileUtil.readableFileSize(totalMemory))
                .setAvailable(FileUtil.readableFileSize(availableMemory))
                .setUsed(FileUtil.readableFileSize(usedMemory))
                .setUsePercent(NumberUtil.round((double) usedMemory / totalMemory * 100, 2).doubleValue())
        );

        // OS
        OperatingSystem.OSVersionInfo osVersion = os.getVersionInfo();
        String codeName = osVersion.getCodeName();
        result.setOs(new SystemInfo.Os()
                .setFamily(os.getFamily())
                .setVersion(osVersion.getVersion())
                //osVersion.getKernelVersion()
                .setKernelVersion(codeName)
        );

        // Network
        result.setNetworks(networkIFs.stream().map(networkIF -> {
            // 网卡理论速率（Mbps）
            long speedBits = networkIF.getSpeed();
            String speed = formatNetworkSpeed(speedBits);
            
            // 累计发送和接收字节数（转换为可读格式）
            String totalSent = FileUtil.readableFileSize(networkIF.getBytesSent());
            String totalRecv = FileUtil.readableFileSize(networkIF.getBytesRecv());
            
            // 识别虚拟网卡
            String networkName = networkIF.getName().toLowerCase();
            boolean isVirtual = isVirtualNetwork(networkName);
            String networkType = isVirtual ? "虚拟网卡" : "物理网卡";
            
            return new SystemInfo.Network()
                    .setName(networkIF.getName())
                    .setIp(StrUtil.join(",", Arrays.asList(networkIF.getIPv4addr())))
                    .setMac(networkIF.getMacaddr())
                    .setStatus(networkIF.getIfOperStatus().toString())
                    .setSpeed(speed)
                    .setUpSpeed(totalSent)  // 累计发送
                    .setDownSpeed(totalRecv) // 累计接收
                    .setVirtual(isVirtual)
                    .setType(networkType);
        }).collect(Collectors.toList()));

        // Disk
        File[] roots = File.listRoots();
        result.setDisks(diskStores.stream().map(diskStore -> {
            long totalDisk = diskStore.getSize();
            long freeDisk = diskStore.getSize() - diskStore.getWriteBytes(); // 近似可用空间
            long usedDisk = totalDisk - freeDisk;
            double usePercent = totalDisk > 0 ? (double) usedDisk / totalDisk * 100 : 0;
            String mountPoint = "";
            for (java.io.File root : roots) {
                if (diskStore.getName() != null && root.getAbsolutePath().toLowerCase().contains(diskStore.getName().toLowerCase())) {
                    mountPoint = root.getAbsolutePath();
                    break;
                }
            }
            return new SystemInfo.Disk()
                    .setName(diskStore.getName())
                    .setType(diskStore.getModel())
                    .setTotal(FileUtil.readableFileSize(totalDisk))
                    .setAvailable(FileUtil.readableFileSize(freeDisk))
                    .setUsed(FileUtil.readableFileSize(usedDisk))
                    .setUsePercent(NumberUtil.round(usePercent, 2).doubleValue())
                    .setMountPoint(mountPoint);
        }).collect(Collectors.toList()));

        log.info(JSONUtil.toJsonPrettyStr(result));
        return result;
    }

    /**
     * 判断是否为虚拟网卡
     * @param networkName 网卡名称
     * @return 是否为虚拟网卡
     */
    private static boolean isVirtualNetwork(String networkName) {
        // 常见的虚拟网卡标识
        String[] virtualKeywords = {
            "vmware", "virtualbox", "vbox", "hyper-v", "hyperv",
            "docker", "veth", "br-", "bridge", "tun", "tap",
            "loopback", "lo", "isatap", "teredo", "6to4",
            "microsoft", "microsoft wi-fi", "microsoft wi-fi direct",
            "bluetooth", "bluetooth network", "wi-fi direct",
            "virtual", "vpn", "ppp", "pppoe", "pptp", "l2tp",
            "hamachi", "zerotier", "openvpn", "wireguard",
            "wsl", "wsl2", "windows subsystem", "subsystem"
        };
        
        for (String keyword : virtualKeywords) {
            if (networkName.contains(keyword)) {
                return true;
            }
        }
        
        // 检查MAC地址模式（虚拟网卡通常有特定的MAC地址前缀）
        return false;
    }

    /**
     * 格式化网络速率
     * @param speedBits 速率（bit/s）
     * @return 格式化的速率字符串
     */
    private static String formatNetworkSpeed(long speedBits) {
        if (speedBits <= 0) {
            return "未知";
        }
        
        // 转换为 Mbps
        double speedMbps = speedBits / 1_000_000.0;
        
        if (speedMbps >= 1000) {
            return String.format("%.1f Gbps", speedMbps / 1000);
        } else if (speedMbps >= 1) {
            return String.format("%.0f Mbps", speedMbps);
        } else {
            return String.format("%.0f Kbps", speedBits / 1000.0);
        }
    }
}